using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using ViewOnCore;
using ViewOnCore.HostContract.Media;
using ViewOnCore.HostContract.Tasks;
using ViewOnCore.HostContract;
using ViewOnCore.Visual;
using ViewOnCore.Helper.WPF;
using Un4seen.Bass.Misc;

namespace MiniPlayerAddIn
{

    public partial class PlayerBar : VisualPart
    {

        #region Variables

        private ColorSelector _colorSelector;

        //---- Spectrum
        private Visuals _visuals = new Visuals();
        private BitmapSource _bitmapSource;

        //---- Tasks history
        private List<Task> TitleBarTextHistory = new List<Task>();

        #endregion

        #region Constructor

        public PlayerBar()
        {
            InitializeComponent();

            MediaTypeTags.Add("Music");
            MediaTypeTags.Add("Radio");

            _colorSelector = new ColorSelector(Host.UIService.Color02.Color);
            _colorSelector.PlacementTarget = btnColorSelector;
            _colorSelector.ColorChanged += new ColorChangedHandler(colorSelector_ColorChanged);

            btnColorSelector.Click += new RoutedEventHandler(this.colorSelector_Click);

            this.Loaded += new RoutedEventHandler(PlayerBarView2_Loaded);

            //---- Events
            Host.MediaService.PlayerStatusChanged += new EventHandler(MediaService_PlayerStatusChanged);
            Host.MediaService.MediaSelectionChanged += new EventHandler(MediaService_MediaSelectionChanged);
            Host.TaskStarted += new EventHandler(Host_TaskStarted);
            Host.TaskCompleted += new EventHandler(Host_TaskCompleted);
            Host.MediaService.MediaCannotConnect += new EventHandler(MediaService_MediaDataCannotConnect);
            Host.MediaService.MediaDeviceActivation += new EventHandler(MediaService_MediaDeviceActivation);
            Host.MediaService.MediaVisualRefresh += new EventHandler(MediaService_MediaVisualRefresh);
        }

        #endregion

        #region Properties

        override public string Handle
        {
            get
            {
                return "MiniPlayerBar";
            }
        }

        override public VisualPartMode Mode
        {
            get
            {
                return VisualPartMode.Mini;
            }
        }

        #endregion

        #region colorSelector_Click

        private void colorSelector_Click(object sender, RoutedEventArgs e)
        {
            _colorSelector.IsOpen = true;
        }

        #endregion

        #region Event : colorSelector - ColorChanged

        private void colorSelector_ColorChanged(System.Windows.Media.Color color)
        {
            Host.UIService.SetBaseColor(color);
        }

        #endregion

        #region VisualPart : OnPreDisplay

        override public void OnPreDisplay(Control container)
        {
            OnVolumeChanged();
            OnUpdateDisplay();
        }

        #endregion

        #region VisualPart : OnRequestDisplayInformation

        override public VisualPartDisplayInformation OnRequestDisplayInformation(VisualPartDisplayInformation currentDisplayInformation, VisualPartDisplayInformation leavedDisplayInformation)
        {
            VisualPartDisplayInformation newDisplayInformation = new VisualPartDisplayInformation();

            if (Host.MediaService.CurrentMediaDeviceFeature.Handle.Contains("Radio"))
                newDisplayInformation.Width = 510;
            else
                newDisplayInformation.Width = 710;

            newDisplayInformation.Height = 20;

            return newDisplayInformation;
        }

        #endregion

        #region MediaService : Events

        void MediaService_MediaDeviceActivation(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnMediaDeviceActivation(); });
        }

        void MediaService_PlayerStatusChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnPlayerStatusChanged(); });
        }

        void MediaService_MediaSelectionChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnMediaSelectionChanged(); });
        }

        void MediaService_MediaDataCannotConnect(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnMediaDataCannotConnect(); });
        }

        void MediaService_MediaVisualRefresh(object sender, EventArgs e)
        {
            // "BeginInvoke" is important to avoid dead lock
            // of the Bass timer... there is an issue with this.
            Dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DelegateInvoker)delegate { OnUpdateDisplay(); });
        }

        #endregion

        #region Tasks management

        void Host_TaskStarted(object sender, EventArgs e)
        {
            if (!IsActive || !VisualPartHelper.IsCurrentMediaDeviceSupported(this.MediaTypeTags))
                return;

            // Keep the history of the tasks
            lock (TitleBarTextHistory)
                TitleBarTextHistory.Add(sender as Task);

            Dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DelegateInvoker)delegate { AsyncStartWaiting(sender as Task); });
        }

        void Host_TaskCompleted(object sender, EventArgs e)
        {
            if (!IsActive || !VisualPartHelper.IsCurrentMediaDeviceSupported(this.MediaTypeTags))
                return;

            // Remove from the history
            lock (TitleBarTextHistory)
                TitleBarTextHistory.Remove(sender as Task);

            Dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DelegateInvoker)delegate { AsyncStopWaiting(sender as Task); });
        }

        private void AsyncStartWaiting(Task startedTask)
        {
            OnStartWaiting(startedTask);
        }

        private void AsyncStopWaiting(Task completedTask)
        {
            OnStopWaiting(completedTask);
        }

        #endregion

        #region VolumeChanged

        private void VolumeChanged(object sender, RoutedPropertyChangedEventArgs<double> args)
        {
            if (Host.MediaService.Volume != (int)slider.Value)
                Host.MediaService.Volume = (int)slider.Value;
        }

        #endregion

        #region PositionChanged

        private void PositionChanged(object sender, RoutedPropertyChangedEventArgs<double> args)
        {
            OnPositionChanged((float)sliPosition.Value);
        }

        #endregion

        #region OnPositionChanged

        public void OnPositionChanged(float position)
        {
            Host.MediaService.Position = position;
        }

        #endregion

        #region Buttons events

        public void btnPrevious_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.PreviousMedia();
        }

        public void btnNext_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.NextMedia();
        }

        public void btnPlay_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.Play();
        }

        public void btnMute_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.MuteInverse();

            if (Host.MediaService.IsMuted)
            {
                patMuteOff.Visibility = Visibility.Visible;
                patMuteOn.Visibility = Visibility.Collapsed;
            }
            else
            {
                patMuteOff.Visibility = Visibility.Collapsed;
                patMuteOn.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region OnMediaDeviceActivation

        public void OnMediaDeviceActivation()
        {
            //---- Update the controls and resize
            if (!Host.MediaService.CurrentMediaDeviceFeature.IsNextPreviousSupported)
            {
                btnPrevious.Visibility = Visibility.Collapsed;
                btnNext.Visibility = Visibility.Collapsed;
                sliPosition.Visibility = Visibility.Collapsed;
                txtDuration.Visibility = Visibility.Collapsed;
            }
            else
            {
                btnPrevious.Visibility = Visibility.Visible;
                btnNext.Visibility = Visibility.Visible;
                sliPosition.Visibility = Visibility.Visible;
                txtDuration.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region OnVolumeChanged

        public void OnVolumeChanged()
        {
            if (Host.MediaService.Volume != (int)slider.Value)
                slider.Value = Host.MediaService.Volume;
        }

        #endregion

        #region OnPlayerStatusChanged

        public void OnPlayerStatusChanged()
        {
            if (Host.MediaService.MediaPlayStatus == MediaPlayStatus.Play)
            {
                patPause.Visibility = Visibility.Visible;
                patPlay.Visibility = Visibility.Collapsed;
            }
            else
            {
                patPause.Visibility = Visibility.Collapsed;
                patPlay.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region OnMediaSelectionChanged

        public void OnMediaSelectionChanged()
        {
            if (Host.MediaService.CurrentMedia == null)
            {
                txtTitle.Text = "-";
                txtDuration.Text = "-";
                return;
            }

            txtTitle.Text = Host.MediaService.CurrentMedia.Title;

            Music music = Host.MediaService.CurrentMedia as Music;
            if (music != null)
                txtDuration.Text = string.Format("- / {0:00}:{1:00}", (int)music.Duration.Minutes, (int)music.Duration.Seconds);
            else
                txtDuration.Text = "- / -";
        }

        #endregion

        #region OnMediaDataCannotConnect

        public void OnMediaDataCannotConnect()
        {
        }

        #endregion

        #region Task : waiting / stoping

        public void OnStartWaiting(Task startedTask)
        {
            if (spiRadio.IsRunning)
                return;

            griInformations.Visibility = Visibility.Hidden;
            spiRadio.Start();
            UpdateTextZone();
        }

        public void OnStopWaiting(Task completedTask)
        {
            if (Host.TasksCount > 0)
                return;

            spiRadio.Stop();
            griInformations.Visibility = Visibility.Visible;
            UpdateTextZone();
        }

        private void UpdateTextZone()
        {
            lock (TitleBarTextHistory)
                if (TitleBarTextHistory.Count > 0)
                {
                    txtTitle.Visibility = Visibility.Hidden;
                }
                else
                {
                    txtTitle.Visibility = Visibility.Visible;
                }
        }

        #endregion

        #region OnUpdateDisplay

        public void OnUpdateDisplay()
        {
            //---- Position
            sliPosition.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(PositionChanged);
            try
            {
                sliPosition.Value = Host.MediaService.Position;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            sliPosition.ValueChanged += new RoutedPropertyChangedEventHandler<double>(PositionChanged);

            //---- Volume
            slider.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(VolumeChanged);
            try
            {
                slider.Value = Host.MediaService.Volume;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            slider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(VolumeChanged);

            //---- Time
            Music song = Host.MediaService.CurrentMedia as Music;
            if (song == null)
            {
                txtDuration.Text = "- : -";
            }
            else
            {
                TimeSpan totalTime = Host.MediaService.TotalTime;
                txtDuration.Text = string.Format("{0:00}:{1:00} / {2:00}:{3:00}", totalTime.Minutes, totalTime.Seconds, (int)song.Duration.Minutes, (int)song.Duration.Seconds);
            }

            //---- Draw the spectrum
            try
            {
                DrawSpectrum();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        #endregion

        #region DrawSpectrum

        private void DrawSpectrum()
        {
            if (this.Visibility != Visibility.Visible)
                return;

            Bitmap bitmap = CreateSpectrumBitmap();
            if (bitmap == null)
            {
                SpectrumImage.Source = null;
                return;
            }

            IntPtr hbitmap = bitmap.GetHbitmap();
            _bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            DeleteObject(hbitmap);
            bitmap.Dispose();
            SpectrumImage.Source = _bitmapSource;
        }

        //private int voicePrintIdx = 0;
        private Bitmap CreateSpectrumBitmap()
        {
            System.Drawing.Color color1 = System.Drawing.Color.FromArgb(Host.UIService.Color01.Color.R, Host.UIService.Color01.Color.G, Host.UIService.Color01.Color.B);
            System.Drawing.Color color3 = System.Drawing.Color.FromArgb(Host.UIService.Color02.Color.R, Host.UIService.Color02.Color.G, Host.UIService.Color02.Color.B);
            color1 = color3;
            System.Drawing.Color color2 = System.Drawing.Color.FromArgb(Host.UIService.Color03.Color.R, Host.UIService.Color03.Color.G, Host.UIService.Color03.Color.B);

            int height = (int)SpectrumImage.Height;

            int stream = Host.MediaService.InternalStreamHandle;

            switch (Host.SettingsService.SpectrumId)
            {

                case 0: // normal spectrum (width = resolution)
                    return _visuals.CreateSpectrum(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, false, false, false);

                case 1: // normal spectrum (full resolution)
                    return _visuals.CreateSpectrum(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, false, true, true);

                case 2: // line spectrum (width = resolution)
                    return _visuals.CreateSpectrumLine(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 2, 2, false, false, false);

                case 3: // line spectrum (full resolution)
                    return _visuals.CreateSpectrumLine(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 16, 4, false, true, true);

                //case 4: // ellipse spectrum (width = resolution)
                //    return _visuals.CreateSpectrumEllipse(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 1, 2, false, false, false);

                case 4: // ellipse spectrum (full resolution)
                    return _visuals.CreateSpectrumEllipse(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 2, 4, false, true, true);

                case 5: // dot spectrum (width = resolution)
                    return _visuals.CreateSpectrumDot(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 1, 0, false, false, false);

                case 6: // dot spectrum (full resolution)
                    return _visuals.CreateSpectrumDot(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 2, 1, false, false, true);

                case 7: // peak spectrum (width = resolution)
                    return _visuals.CreateSpectrumLinePeak(stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 3, 1, 2, 10, false, false, false);
                //return _vis.CreateSpectrumLinePeak(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, System.Drawing.Color.SeaGreen, System.Drawing.Color.LightGreen, System.Drawing.Color.Orange, System.Drawing.Color.Black, 2, 1, 2, 10, false, false, false);

                case 8: // wave spectrum (width = resolution)
                    return _visuals.CreateSpectrumWave(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 1, false, false, false);

                case 9: // dancing beans spectrum (width = resolution)
                    return _visuals.CreateSpectrumBean(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 4, false, false, true);

                case 10: // dancing text spectrum (width = resolution)
                    return _visuals.CreateSpectrumText(stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, "----------", false, false, true);

                case 11:
                    return _visuals.CreateWaveForm(stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 1, true, false, true);

                case 12:
                    return null;

                //case 9:// peak spectrum (full resolution)
                //    return _visuals.CreateSpectrumLinePeak(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 23, 5, 3, 5, false, true, true);

                // frequency detection
                //case 14:
                //    float amp = _vis.DetectFrequency(BassPlayer.Stream, 10, 500, true);
                //    if (amp > 0.3)
                //        SpectrumImage.BackColor = System.Drawing.Color.Red;
                //    else
                //        SpectrumImage.BackColor = System.Drawing.Color.Black;
                //    break;
                //// 3D voice print
                //case 15:
                //    // we need to draw directly directly on the picture box...
                //    // normally you would encapsulate this in your own custom control
                //    Graphics g = Graphics.FromHwnd(SpectrumImage.Handle);
                //    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                //    _vis.CreateSpectrum3DVoicePrint(BassPlayer.Stream, g, new Rectangle(0, 0, (int)SpectrumImage.Width, (int)height), System.Drawing.Color.Black, System.Drawing.Color.White, voicePrintIdx, false, false);
                //    g.Dispose();
                //    // next call will be at the next pos
                //    voicePrintIdx++;
                //    if (voicePrintIdx > SpectrumImage.Width - 1)
                //        voicePrintIdx = 0;
                //    break;
                // WaveForm
                //case 16:
                //	return _visuals.CreateWaveForm(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 1, true, false, true);
            }

            return null;
        }

        #endregion

        #region spectrum_MouseUp

        public void spectrum_MouseUp(object sender, MouseEventArgs args)
        {
            Host.SettingsService.SpectrumId++;
            if (Host.SettingsService.SpectrumId > 12)
                Host.SettingsService.SpectrumId = 0;
        }

        #endregion

        #region API

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("winmm.dll")]
        public static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume);

        #endregion

        #region PlayerBar_Loaded

        void PlayerBarView2_Loaded(object sender, RoutedEventArgs e)
        {
            //---- Mini player events
            ((Button)SearchFrameworkElement("miniMode")).Click += new RoutedEventHandler(this.miniMode_Click);
            //((Button)SearchFrameworkElement("minimize")).Click += new RoutedEventHandler(this.minimize_Click);
            ((Button)SearchFrameworkElement("close")).Click += new RoutedEventHandler(this.close_Click);
            ((Border)SearchFrameworkElement("borMain")).MouseLeftButtonDown += new MouseButtonEventHandler(background_MouseLeftButtonDown);

            //---- Remove the event, to avoid recalling it (Due to mini player manipulation)
            this.Loaded -= new RoutedEventHandler(PlayerBarView2_Loaded);
        }

        #endregion

        #region Events : Mini player

        private void miniMode_Click(object sender, RoutedEventArgs e)
        {
            Host.VisualService.VisualPartMode = VisualPartMode.Normal;
        }

        private void minimize_Click(object sender, RoutedEventArgs e)
        {
            Host.ApplicationWindow.WindowState = WindowState.Minimized;
        }

        private void close_Click(object sender, RoutedEventArgs e)
        {
            Host.ApplicationWindow.Close();
        }

        private void background_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            HitTestResult hRresult = VisualTreeHelper.HitTest(Host.ApplicationWindow, e.GetPosition(this));

            if (hRresult.VisualHit != null &&
                hRresult.VisualHit is Border &&
                ((Border)hRresult.VisualHit).Name == "borSpectrumHighlight")
                return;

            Host.ApplicationWindow.DragMove();
        }

        #endregion

        #region Helpers

        object SearchFrameworkElement(string name)
        {
            object obj = Template.FindName(name, this);
            if (obj != null)
                return obj;
            return FindName(name);
        }

        #endregion

    }
}